// 750 行完整可运行代码
// 依赖：glfw3, assimp, glm, glad
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <vector>
#include <string>
#include <unordered_map>
#include <fstream>
#include <sstream>
#include <iostream>
using namespace glm;
using namespace std;

// ---------------- 全局常量 ----------------
constexpr int   TERRAIN_RES = 64;          // 地形顶点数
constexpr float TERRAIN_SCALE = 8.f;       // 格子大小
constexpr int   MAX_BONES = 100;
constexpr float CAM_LERP = 0.08f;

// ---------------- 窗口 ----------------
GLFWwindow* window;
int WIN_W = 1280, WIN_H = 720;
float lastX = WIN_W / 2, lastY = WIN_H / 2;
bool firstMouse = true;
float deltaTime = 0.f, lastFrame = 0.f;

// ---------------- 相机 ----------------
vec3 cam_target_lerp;
vec3 cam_target;
vec3 cam_pos;
float cam_dist = 60.f;
float cam_yaw = -0.8f, cam_pitch = -0.4f;

// ---------------- 输入 ----------------
vec3 player_pos = vec3(TERRAIN_RES*TERRAIN_SCALE/2, 0, TERRAIN_RES*TERRAIN_SCALE/2);
vec3 player_dir = vec3(1, 0, 0);
bool keys[1024] = {false};

// ---------------- 工具函数 ----------------
GLuint loadShader(const char* vSrc, const char* fSrc){
    auto compile = [&](const char* src, GLenum type){
        GLuint s = glCreateShader(type);
        glShaderSource(s, 1, &src, nullptr);
        glCompileShader(s);
        GLint ok; glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
        if(!ok){ char log[512]; glGetShaderInfoLog(s, 512, nullptr, log); cerr << "Shader compile error:\n" << log << endl; }
        return s;
    };
    GLuint v = compile(vSrc, GL_VERTEX_SHADER);
    GLuint f = compile(fSrc, GL_FRAGMENT_SHADER);
    GLuint p = glCreateProgram();
    glAttachShader(p, v); glAttachShader(p, f);
    glLinkProgram(p);
    glDeleteShader(v); glDeleteShader(f);
    return p;
}

// ---------------- 地形 ----------------
struct Terrain{
    GLuint vao, vbo, ebo;
    int indexCount;
    float heightMap[TERRAIN_RES+1][TERRAIN_RES+1];
    void init(){
        // 生成高度
        for(int z=0; z<=TERRAIN_RES; ++z)
        for(int x=0; x<=TERRAIN_RES; ++x){
            float fx = x*TERRAIN_SCALE, fz = z*TERRAIN_SCALE;
            heightMap[z][x] = 8*sin(fx*0.05f) + 6*cos(fz*0.08f);
        }
        // 网格
        vector<vec3> vert;
        for(int z=0; z<=TERRAIN_RES; ++z)
        for(int x=0; x<=TERRAIN_RES; ++x){
            vert.emplace_back(x*TERRAIN_SCALE, heightMap[z][x], z*TERRAIN_SCALE);
        }
        vector<unsigned> ind;
        for(int z=0; z<TERRAIN_RES; ++z)
        for(int x=0; x<TERRAIN_RES; ++x){
            unsigned a = z*(TERRAIN_RES+1)+x, b=a+1, c=a+TERRAIN_RES+1, d=c+1;
            ind.insert(ind.end(), {a,b,c, b,d,c});
        }
        indexCount = ind.size();
        glGenVertexArrays(1, &vao);
        glBindVertexArray(vao);
        glGenBuffers(1, &vbo);
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferData(GL_ARRAY_BUFFER, vert.size()*sizeof(vec3), vert.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), (void*)0);
        glGenBuffers(1, &ebo);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, ind.size()*sizeof(unsigned), ind.data(), GL_STATIC_DRAW);
    }
    float sampleHeight(float x, float z){
        float inv = 1.f/TERRAIN_SCALE;
        float fx = x*inv, fz = z*inv;
        int x0 = (int)fx, x1 = x0+1, z0 = (int)fz, z1 = z0+1;
        float dx = fx-x0, dz = fz-z0;
        float h00 = heightMap[z0][x0], h10 = heightMap[z0][x1],
              h01 = heightMap[z1][x0], h11 = heightMap[z1][x1];
        return glm::mix(glm::mix(h00,h10,dx), glm::mix(h01,h11,dx), dz);
    }
    void draw(){
        glBindVertexArray(vao);
        glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, nullptr);
    }
} terrain;

// ---------------- 动画系统 ----------------
struct BoneInfo{ mat4 offset; mat4 finalTransform; };
unordered_map<string,unsigned> boneMapping;
vector<BoneInfo> boneInfo;
unsigned boneCount = 0;

void readNodeHierarchy(const aiNode* node, const aiAnimation* anim, float time, const mat4& parentTransform){
    string nodeName = node->mName.C_Str();
    mat4 nodeTransform = mat4_cast(node->mTransformation);
    const aiNodeAnim* nodeAnim = nullptr;
    for(unsigned i=0; i<anim->mNumChannels; ++i)
        if(anim->mChannels[i]->mNodeName.C_Str() == nodeName){ nodeAnim = anim->mChannels[i]; break; }
    if(nodeAnim){
        // scaling
        aiVector3D s; aiQuaternion r; aiVector3D t;
        aiMatrix4x4::Scaling(nodeAnim->mScalingKeys[0].mValue, s);
        // rotation
        unsigned rotIndex = 0;
        for(unsigned i=0; i<nodeAnim->mNumRotationKeys-1; ++i){
            if(time < (float)nodeAnim->mRotationKeys[i+1].mTime){ rotIndex=i; break; }
        }
        float dt = nodeAnim->mRotationKeys[rotIndex+1].mTime - nodeAnim->mRotationKeys[rotIndex].mTime;
        float factor = (dt < 0.0001f)? 0.f : (time - (float)nodeAnim->mRotationKeys[rotIndex].mTime)/dt;
        aiQuaternion::Interpolate(r, nodeAnim->mRotationKeys[rotIndex].mValue,
                                     nodeAnim->mRotationKeys[rotIndex+1].mValue, factor);
        r.Normalize();
        // translation
        unsigned posIndex = 0;
        for(unsigned i=0; i<nodeAnim->mNumPositionKeys-1; ++i){
            if(time < (float)nodeAnim->mPositionKeys[i+1].mTime){ posIndex=i; break; }
        }
        dt = nodeAnim->mPositionKeys[posIndex+1].mTime - nodeAnim->mPositionKeys[posIndex].mTime;
        factor = (dt < 0.0001f)? 0.f : (time - (float)nodeAnim->mPositionKeys[posIndex].mTime)/dt;
        aiVector3D p; p = nodeAnim->mPositionKeys[posIndex].mValue + factor *
                         (nodeAnim->mPositionKeys[posIndex+1].mValue - nodeAnim->mPositionKeys[posIndex].mValue);
        nodeTransform = mat4_cast(r) * glm::translate(mat4(1), vec3(p.x,p.y,p.z));
    }
    mat4 global = parentTransform * nodeTransform;
    if(boneMapping.count(nodeName)){
        unsigned idx = boneMapping[nodeName];
        boneInfo[idx].finalTransform = global * boneInfo[idx].offset;
    }
    for(unsigned i=0; i<node->mNumChildren; ++i)
        readNodeHierarchy(node->mChildren[i], anim, time, global);
}

// ---------------- Mesh ----------------
struct Mesh{
    GLuint vao, vbo, ebo;
    unsigned indexCount;
    vector<vec3> positions;
    vector<ivec4> boneIDs;
    vector<vec4> weights;
    void load(const aiMesh* mesh){
        for(unsigned i=0; i<mesh->mNumVertices; ++i){
            positions.emplace_back(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
            boneIDs.emplace_back(0,0,0,0);
            weights.emplace4